package cate.game.role.pvp.arena;

import cate.common.table.d.GDFight;
import cate.common.table.d.GDFunc;
import cate.common.table.d.GDOperation;
import cate.common.table.d.GDPosition;
import cate.common.table.pvp.arena.challenge_reward.ArenaChallengeRewardRow;
import cate.common.table.pvp.arena.daily_fight_reward.ArenaDailyFightRewardRow;
import cate.common.table.pvp.arena.robot_storage.ArenaRobotStorageRow;
import cate.common.util.TimeTool;
import cate.game.play.part.FightSide;
import cate.game.play.part.PlaySide;
import cate.game.play.proce.play.PlayPO;
import cate.game.play.vo.PlayReportVO;
import cate.game.play.vo.ReplayUtils;
import cate.game.pvp.arena.ArenaConstants;
import cate.game.pvp.arena.core.ArenaFightRecord;
import cate.game.pvp.arena.core.ArenaReplayProfile;
import cate.game.pvp.arena.core.ArenaRoleDetail;
import cate.game.pvp.arena.core.ArenaRoleSnapshot;
import cate.game.pvp.arena.msg.AIRefreshOpponents;
import cate.game.pvp.arena.msg.AIReloadRankAndScore;
import cate.game.pvp.arena.msg.AIReportFightResult;
import cate.game.pvp.arena.msg.AISyncRoleForm;
import cate.game.res.MixRes;
import cate.game.res.MixResAdder;
import cate.game.res.MixResConsumer;
import cate.game.role.Role;
import cate.game.role.RoleSnapshot;
import cate.game.role.farm.AbstractFarm;
import cate.game.role.fight.FuncPosition;
import cate.game.role.pvp.arena.msg.*;
import cate.game.videohall.po.ReplayProfile;
import cp.solution.manage.api.open.replay.ReplaySaveResp;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.annotation.Transient;

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@NoteClass("跨服擂台赛数据")
public class RoleArena extends AbstractFarm {
    @NoteField("已用的免费次数")
    public int freeTimeUsed;
    @NoteField("今日挑战次数")
    public int dailyFightNum;
    @NoteField("每日挑战奖励领取状态")
    public Map<Integer, Boolean> dailyFightRewards;
    @NoteField("是否跳过战斗")
    public boolean skipFight;
    @NoteField("点赞已使用次数")
    public int likeTimeUsed;
    @NoteField("点赞过的玩家UID")
    public List<String> likeUIds;
    @NoteField("上次对手刷新时间")
    public long lastOpponentRefreshTime;
    @NoteField("对手列表")
    public List<ArenaRoleSnapshot> opponents;
    @NoteField("防守视野")
    public List<Boolean> defendVisions;
    @NoteField("战报列表")
    public List<ArenaFightRecord> records;

    @Transient
    @NoteField("我的排行")
    public int rank;
    @Transient
    @NoteField("我的积分")
    public int score;
    @Transient
    @NoteField("下次阵容同步时间")
    private long nextSyncDefendTime;

    @NoteField("缓存的胜利方")
    public List<Byte> preWinner;
    @NoteField("对手具体信息")
    public ArenaRoleDetail opponentDetail;
    @Transient
    @NoteField("战斗结果类型")
    public int resultType;
    @Transient
    @NoteField("战斗结果奖励")
    public Map<Integer, MixRes> resultRewards;

    @Override
    public int getFuncId() {
        return GDFunc.ARENA;
    }

    @Override
    public void onAfterOpen() {
        // 切周或者没刷过对手的刷一下
        if (lastOpponentRefreshTime == 0 || TimeTool.isDifferentWeek(lastOpponentRefreshTime, System.currentTimeMillis())) {
            refreshOpponents(true, false);
        }
        reloadRankAndScore(true);
        // 同布人物快照
        role.getRes().crossSync.syncSnapshot();
    }

    @Override
    public int getFightFuncId() {
        return GDFunc.ARENA;
    }

    @Override
    public void initialize(Role role) {
        super.initialize(role);
        if (dailyFightRewards == null) {
            dailyFightRewards = new HashMap<>();
        }
        reloadDailyFightRewards();
        if (likeUIds == null) {
            likeUIds = new ArrayList<>(3);
        }
        if (preWinner == null) {
            preWinner = new ArrayList<>(3);
        }
        if (records == null) {
            records = new ArrayList<>();
        }
        nextSyncDefendTime = System.currentTimeMillis();
    }

    @Override
    public void onEnter() {
        super.onEnter();
        if (!isOpen()) {
            return;
        }
        // 切周或者没刷过对手的刷一下
        if (lastOpponentRefreshTime == 0 || TimeTool.isDifferentWeek(lastOpponentRefreshTime + TimeTool.TimeCons.HOUR,
                System.currentTimeMillis() + TimeTool.TimeCons.HOUR)) {
            refreshOpponents(true, false);
        }
        reloadRankAndScore(false);
    }

    @Override
    protected void offline() {
        // 下线自动帮忙关了奖励面板吧
        closeFightResult();
    }

    /**
     * 发送信息到前端
     */
    public void noticeUpdate() {
        role.sendNow(new ArenaInfoResp(this));
    }

    /**
     * 循环
     */
    public void tick() {
        long nowTime = System.currentTimeMillis();
        if (nowTime >= nextSyncDefendTime) {
            syncDefend(false);
            reloadRankAndScore(false);
        }
    }

    /**
     * 重新加载每日挑战奖励
     */
    private void reloadDailyFightRewards() {
        dailyFightRewards.entrySet()
                .removeIf(e -> role.getGame().table.arena.daily_fight_reward.get(e.getKey()) == null);
        role.getGame().table.arena.daily_fight_reward.getList().forEach(row -> {
            if (!dailyFightRewards.containsKey(row.id)) {
                dailyFightRewards.put(row.id, false);
            }
        });
    }

    @Override
    public void onDaySpan(boolean silence) {
        super.onDaySpan(silence);
        freeTimeUsed = 0;
        dailyFightNum = 0;
        dailyFightRewards.entrySet().forEach(e -> e.setValue(false));
        likeTimeUsed = 0;
        likeUIds.clear();
        if (!silence) {
            noticeUpdate();
        }
    }

    /**
     * 检测功能总配队是否合理
     */
    public Predicate<FuncPosition> allowFuncPosition() {
        return funcPosition -> {
            for (int i = 1; i <= 3; i++) {
                if (funcPosition.getPosition(i) == null) {
                    return false;
                }
            }
            return true;
        };
    }

    /**
     * 同步防守布阵
     *
     * @param onlyCheck 是否仅用于检测
     * @return 是否同布成功
     */
    private boolean syncDefend(boolean onlyCheck) {
        boolean fail = false;
        List<PlaySide> playSides = new ArrayList<>(3);
        for (int pos = 1; pos <= 3; pos++) {
            EcResult<PlaySide> r_side = role.getGame().fight.side.createPlaySideByRole(role, GDFunc.ARENA_DEFEND, pos);
            if (!r_side.ok()) {
                fail = true;
                break;
            }
            playSides.add(r_side.data);
        }
        if (!fail && !onlyCheck) {
            RoleSnapshot snapshot = new RoleSnapshot(role);
            AISyncRoleForm msg = new AISyncRoleForm(snapshot, defendVisions, playSides);
            role.getGame().pvp.arena.syncRoleForm(msg);
        }
        // 下一次同步防守阵容时间为随机10-12分钟之后
        if (!onlyCheck) {
            nextSyncDefendTime += new Random().nextInt((int) (2 * TimeTool.TimeCons.MINUTE)) + 10 * TimeTool.TimeCons.MINUTE;
        }
        return !fail;
    }

    /**
     * 重新加载排行和积分
     */
    public void reloadRankAndScore(boolean forceSend) {
        AIReloadRankAndScore msg = role.getGame().pvp.arena.reloadRankAndScore(role.getUid());
        if (msg != null && role.getUid().equals(msg.uid)) {
            boolean realSend = forceSend || (rank != msg.rank || score != msg.score);
            rank = msg.rank;
            score = msg.score;
            if (realSend) {
                noticeUpdate();
            }
        }
    }

    /**
     * 查看对手详情
     *
     * @param uid 唯一ID
     */
    public EcResult<Void> viewOpponentDetail(String uid) {
        EcResult<Void> r = new EcResult<>();
        Optional<ArenaRoleSnapshot> optional = opponents.stream().filter(oppo -> uid.equals(oppo.snapshot.uid)).findAny();
        if (!optional.isPresent()) {
            return r.fail("对手不在挑战列表");
        }
        opponentDetail = game().pvp.arena.getRoleDetail(uid);
        if (opponentDetail == null) {
            refreshOpponents(true, true);
            return r.fail("对手信息有误");
        }
        role.sendNow(new ArenaViewDetailResp(opponentDetail));
        return r.success();
    }

    /**
     * 挑战对手
     *
     * @return 挑战结果
     */
    public EcResult<Void> challengeOpponent() {
        EcResult<Void> r = new EcResult<>();
        int weekDay = TimeTool.dayOfWeekCn(System.currentTimeMillis());
        if (!ArenaConstants.OPEN_WEEK_DAYS.contains(weekDay)) {
            return r.fail(ArenaConstants.OPEN_NOTICE);
        }
        if (isFighting()) {
            return r.fail("正在战斗中");
        }
        if (freeTimeUsed >= ArenaConstants.FREE_FIGHT_DAILY) {
            MixRes cost = new MixRes(ArenaConstants.FIGHT_COST_STR);
            if (cost.isEmpty()) {
                return r.fail("挑战消耗配错咧");
            }
            EcResult<?> r_cost = cost.consumeCheck(role);
            if (!r_cost.ok()) {
                return r.fail(r_cost.code, r_cost.message);
            }
        }
        if (opponentDetail == null) {
            return r.fail("还未选定对手");
        }
        FuncPosition funcPosition = role.getFight().getFuncPositions(GDFunc.ARENA);
        if (funcPosition == null || !allowFuncPosition().test(funcPosition)) {
            return r.fail("请设定好进攻阵容");
        }
        FuncPosition defendPosition = role.getFight().getFuncPositions(GDFunc.ARENA_DEFEND);
        if (defendPosition == null || !allowFuncPosition().test(defendPosition) || !syncDefend(true)) {
            // 进攻会顺便设置防守阵容 设置防守阵容
            defendPosition = funcPosition.copy();
            defendPosition.funcId = GDFunc.ARENA_DEFEND;
            role.getGame().fight.position.updatePosition(role, defendPosition);
            defendVisions = ArenaConstants.BUILD_DEFAULT_VISION(rank);
            syncDefend(false);
        }
        r = challenge(GDPosition.DEFAULT_POS_NUM);
        if (r.ok()) {
            dailyFightNum++;
            if (freeTimeUsed >= ArenaConstants.FREE_FIGHT_DAILY) {
                new MixResConsumer<Void>().res(new MixRes(ArenaConstants.FIGHT_COST_STR))
                        .tips(true)
                        .operation(GDOperation.ARENA_CHALLENGE_ENTER, 0)
                        .exe(role);
            } else {
                freeTimeUsed++;
            }
            noticeUpdate();
            role.getHistory().action.arenaChallengeTimes();
        }
        return r;
    }

    /**
     * 挑战指定队伍
     *
     * @param posNum 队伍位置
     * @return 挑战结果
     */
    public EcResult<Void> challenge(byte posNum) {
        EcResult<Void> r = new EcResult<>();
        if (isFighting()) {
            return r.fail("正在战斗中");
        }
        if (opponentDetail == null) {
            return r.fail("未选定对手");
        }
        ArenaCacheFTC context = new ArenaCacheFTC(role, posNum);
        context.playStory.playMax = 3;
        context.skip = skipFight;
        for (int i = 1; i < posNum; i++) {
            if (preWinner.size() <= i) {
                context.playStory.preWinner.add(preWinner.get(i - 1));
            }
        }
        if (opponentDetail.base.snapshot.unreal) {
            // 机器人 读配置
            ArenaRobotStorageRow robotRow = role.getGame().table.arena.robot_storage.get(opponentDetail.robotId);
            if (robotRow == null) {
                return r.fail("找不到对手");
            }
            String insCode = robotRow.getInsCode(posNum);
            if (StringUtils.isBlank(insCode)) {
                return r.fail("找不到对手呀");
            }
            // 挑战模式
            EcResult<FightSide> fsr = role.getGame().fight.side.createFightSideByCode(GDFight.Team.B, insCode);
            if (!fsr.ok()) {
                return r.fail(fsr.message);
            }
            EcResult<PlayPO> fightRes = role.getGame().play.startByRole(role, context, fsr.data);
            if (!fightRes.ok()) {
                return r.fail(fightRes.message);
            }
        } else {
            // 真人 读阵容
            if (opponentDetail.playSides.size() < posNum) {
                return r.fail("对手找不到");
            }
            PlaySide opponent = opponentDetail.playSides.get(posNum - 1);
            FightSide fightSideB = role.getGame().fight.side.createFightSideByPlaySide(GDFight.Team.B, opponent);
            EcResult<PlayPO> fightRes = role.getGame().play.startByRole(role, context, fightSideB);
            if (!fightRes.ok()) {
                return r.fail(fightRes.message);
            }
        }
        return r.success();
    }

    /**
     * 结算结果
     *
     * @param win 是否胜利
     */
    public void handleFinish(boolean win, List<PlayPO> caches) {
        // 保险起见结算下奖励
        closeFightResult();
        ArenaFightResultResp resp = role.getGame().pvp.arena.reportFightResult(
                new AIReportFightResult(role.getUid(), opponentDetail.base.snapshot.uid, win));
        if (resp == null) {
            if (role.getStatus().isOnline()) {
                role.getGame().notice.message(role, "跨服擂台赛战斗结果上报失败!");
            }
            return;
        }
        // 生成战报
        ArenaFightRecord record = new ArenaFightRecord();
        record.winner = win ? GDFight.Team.A : GDFight.Team.B;
        record.self = new RoleSnapshot(role);
        record.selfScore = resp.selfScore;
        record.selfScoreChange = resp.selfScoreChange;
        record.opponent = resp.opponent;
        record.oppoScore = resp.oppoScore;
        record.oppoScoreChange = resp.oppoScoreChange;
        record.replays = caches.stream().map(this::buildArenaReplayProfile).filter(Objects::nonNull).collect(Collectors.toList());
        addFightRecord(record);
        // 生成奖励
        resultType = win ? 1 : 2;
        List<ArenaChallengeRewardRow> rewards = role.getGame().table.arena.challenge_reward.getRandomRewards(resultType, 3);
        if (rewards == null || rewards.size() == 0) {
            if (role.getStatus().isOnline()) {
                role.getGame().notice.message(role, "奖励配炸了!");
            }
            resultType = 0;
            return;
        }
        resultRewards = new HashMap<>();
        for (int i = 0; i < rewards.size(); i++) {
            resultRewards.put(i + 1, new MixRes(rewards.get(i).rewardStr));
        }
        if (role.getStatus().isOnline()) {
            // 在线，则拼个包发给前端
            resp.rewards = resultRewards.entrySet().stream()
                    .sorted(Comparator.comparingInt(Map.Entry::getKey))
                    .map(Map.Entry::getValue).collect(Collectors.toList());
            resp.reports = caches.stream().map(PlayReportVO::new).collect(Collectors.toList());
            role.sendNow(resp);
            // 重新同布一下排名
            reloadRankAndScore(false);
        } else {
            // 不在线，则直接结算
            closeFightResult();
        }
        // 打完强制刷一下对手
        refreshOpponents(true, role.getStatus().isOnline());
    }

    /**
     * 构建跨服擂台赛专用回放简报
     *
     * @param playPO 剧本
     */
    private ArenaReplayProfile buildArenaReplayProfile(PlayPO playPO) {
        try {
            EcResult<ReplaySaveResp> r = role.getGame().openApi().saveReplay(ReplayUtils.toReplaySaveReq(playPO, System.currentTimeMillis() + TimeTool.TimeCons.WEEK));
            if (!r.ok()) {
                return null;
            }
            return new ArenaReplayProfile(r.data.getId(), ReplayProfile.buildProfile(playPO));
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 添加战报
     *
     * @param record 战报
     */
    private void addFightRecord(ArenaFightRecord record) {
        if (records.size() >= ArenaConstants.RECORD_NUM_MAX) {
            int delNum = records.size() + 1 - ArenaConstants.RECORD_NUM_MAX;
            for (int i = 0; i < delNum; i++) {
                ArenaFightRecord rec = records.remove(0);
                if (rec != null) {
                    rec.replays.forEach(replay -> role.getGame().openApi().deleteReplay(replay.openReplayId));
                }
            }
        }
        records.add(record);
    }

    /**
     * 选择领取挑战奖励
     *
     * @param pos 奖励位置
     * @return 领取结果
     */
    public EcResult<Void> chooseFightReward(int pos) {
        EcResult<Void> r = new EcResult<>();
        if (pos < 1 || pos > 3) {
            return r.fail("领啥呢你!!!");
        }
        if (resultRewards == null || !resultRewards.containsKey(pos)) {
            return r.fail("不可重复领取");
        }
        MixRes cost = null;
        if (resultRewards.size() < 3) {
            cost = new MixRes(ArenaConstants.EXTRA_FIGHT_REWARD_COST);
            if (cost.isEmpty()) {
                return r.fail("消耗配错咧!");
            }
            EcResult<?> r_cost = cost.consumeCheck(role);
            if (!r_cost.ok()) {
                return r.fail(r_cost.code, r_cost.message);
            }
        }
        if (cost != null) {
            new MixResConsumer<Void>()
                    .res(cost)
                    .operation(GDOperation.ARENA_CHALLENGE_RESULT, 0)
                    .exe(role);
        }
        MixRes reward = resultRewards.remove(pos);
        new MixResAdder().setRes(reward)
                .setDialogReward(false)
                .setSendMailWhenFull(true)
                .setOperation(GDOperation.ARENA_CHALLENGE_RESULT)
                .setOperationSub(resultType)
                .exe(role);
        role.sendNow(new ArenaFightRewardRemainResp(Stream.of(1, 2, 3)
                .map(index -> !resultRewards.containsKey(index))
                .collect(Collectors.toList()), reward));
        return r.success();
    }

    /**
     * 关闭战斗结算界面
     */
    public void closeFightResult() {
        resultType = 0;
        if (resultRewards != null) {
            if (resultRewards.size() == 3) {
                MixRes first = resultRewards.get(1);
                new MixResAdder().setRes(first)
                        .setDialogReward(true)
                        .setSendMailWhenFull(true)
                        .setOperation(GDOperation.ARENA_CHALLENGE_RESULT)
                        .setOperationSub(resultType)
                        .exe(role);
            }
            resultRewards = null;
        }
    }

    /**
     * 刷新对手列表
     *
     * @param force 是否强制刷新
     */
    public EcResult<Void> refreshOpponents(boolean force, boolean send) {
        EcResult<Void> r = new EcResult<>();
        if (!force) {
            if (System.currentTimeMillis() - lastOpponentRefreshTime < ArenaConstants.REFRESH_OPPONENT_CD * TimeTool.TimeCons.SECOND) {
                return r.fail("刷新CD中");
            }
        }
        AIRefreshOpponents msg = role.getGame().pvp.arena.refreshOpponents(role.getUid());
        if (msg != null && msg.opponents != null) {
            opponents = msg.opponents;
        }
        lastOpponentRefreshTime = System.currentTimeMillis();
        if (send) {
            noticeUpdate();
        }
        return r.success();
    }

    /**
     * 查看排行榜
     */
    public void viewRankList() {
        ArenaViewRankResp resp = role.getGame().pvp.arena.getRankList(role.getUid());
        if (resp == null) {
            role.getGame().notice.message(role, "请求跨服擂台赛排行榜失败!");
            return;
        }
        reloadDailyFightRewards();
        if (resp.self == null) {
            resp.self = new ArenaRoleSnapshot();
            resp.self.rank = rank;
            resp.self.score = score;
            resp.self.snapshot = new RoleSnapshot(role);
        }
        role.sendNow(resp);
    }

    /**
     * 查看大神榜
     */
    public void viewTopList() {
        ArenaViewTopResp resp = role.getGame().pvp.arena.getTopList();
        if (resp == null) {
            role.getGame().notice.message(role, "请求跨服擂台赛大神榜失败!");
            return;
        }
        role.sendNow(resp);
    }

    /**
     * 点赞大神
     *
     * @param uid 大神UID
     * @return 点赞结果
     */
    public EcResult<Void> likeTop(String uid) {
        EcResult<Void> r = new EcResult<>();
        if (likeTimeUsed >= ArenaConstants.LIKE_TIME_DAILY) {
            return r.fail("今日点赞次数已用完");
        }
        if (likeUIds.contains(uid)) {
            return r.fail("不能重复点赞");
        }
        boolean success = role.getGame().pvp.arena.likeTop(uid);
        if (!success) {
            return r.fail("点赞失败");
        }
        MixRes reward = new MixRes(ArenaConstants.LIKE_REWARD);
        if (reward.isEmpty()) {
            return r.fail("点赞奖励配错咧");
        }
        likeTimeUsed++;
        likeUIds.add(uid);
        new MixResAdder().setRes(reward)
                .setDialogReward(true)
                .setSendMailWhenFull(true)
                .setOperation(GDOperation.ARENA_LIKE)
                .exe(role);
        return r.success();
    }

    /**
     * 领取每日挑战奖励
     *
     * @param tid 配置ID
     * @return 领取结果
     */
    public EcResult<Void> receiveDailyFightReward(int tid) {
        EcResult<Void> r = new EcResult<>();
        if (!dailyFightRewards.containsKey(tid)) {
            return r.fail("找不到配置");
        }
        if (dailyFightRewards.get(tid)) {
            return r.fail("不能重复领取");
        }
        ArenaDailyFightRewardRow row = role.getGame().table.arena.daily_fight_reward.get(tid);
        if (row == null) {
            reloadDailyFightRewards();
            noticeUpdate();
            return r.fail("配置找不到");
        }
        if (dailyFightNum < row.fightCount) {
            return r.fail("挑战次数不足");
        }
        MixRes reward = new MixRes(row.rewardStr);
        if (reward.isEmpty()) {
            return r.fail("奖励配错咧");
        }
        dailyFightRewards.put(tid, true);
        new MixResAdder().setRes(reward)
                .setDialogReward(true)
                .setSendMailWhenFull(true)
                .setOperation(GDOperation.ARENA_DAILY_REWARD)
                .setOperationSub(tid)
                .exe(role);
        return r.success();
    }

    /**
     * 设置防守布阵
     *
     * @param defendVisions 阵容视野
     * @param funcPosition 布阵
     * @return 设置结果
     */
    public EcResult<Void> updateDefendPosition(List<Boolean> defendVisions, FuncPosition funcPosition) {
        EcResult<Void> r = new EcResult<>();
        if (defendVisions == null || defendVisions.size() != 3) {
            return r.fail("数据有误");
        }
        if (funcPosition == null || funcPosition.funcId != GDFunc.ARENA_DEFEND) {
            return r.fail("数据有错");
        }
        reloadDailyFightRewards();
        int hideNum = rank == 0 || rank > ArenaConstants.HIDE_2_RANK ? 1 : 2;
        if (defendVisions.stream().filter(b -> b).count() != hideNum) {
//            defendVisions = ArenaConstants.BUILD_DEFAULT_VISION(rank);
//            game().notice.message(role, String.format("需要隐藏%d个队伍", hideNum));
            return r.fail(String.format("需要隐藏%d个队伍", hideNum));
        }
        EcResult<?> r_pos = role.getGame().fight.position.updatePosition(role, funcPosition);
        if (!r_pos.ok()) {
            return r.fail(r_pos.code, r_pos.message);
        }
        this.defendVisions = defendVisions;
        syncDefend(false);
        return r.success();
    }
}
